package cn.anecansaitin.firecrafting.client.util;

import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.platform.GlStateManager.DestFactor;
import com.mojang.blaze3d.platform.GlStateManager.SourceFactor;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import com.mojang.math.Matrix4f;
import com.mojang.math.Quaternion;
import com.mojang.math.Vector3f;
import mcjty.theoneprobe.TheOneProbe;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.decoration.HangingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.fluids.FluidStack;

import java.util.List;

/**
 * Some code copy from CoFH Core, JEI, Minecraft.
 * 部分代码从热力核心、JEI、我的世界原版拷贝
 */
public final class RenderHelper {

    private RenderHelper() {

    }

    public static final ResourceLocation MC_BLOCK_SHEET = new ResourceLocation("textures/atlas/blocks.png");

    public static TextureAtlas textureMap() {

        return Minecraft.getInstance().getModelManager().getAtlas(InventoryMenu.BLOCK_ATLAS);
    }

    public static Tesselator tesselator() {

        return Tesselator.getInstance();
    }

    // region SHEETS
    public static void setBlockTextureSheet() {

        setShaderTexture0(MC_BLOCK_SHEET);
    }

    public static void drawFluid(int x, int y, FluidStack fluid, int width, int height) {
        drawFluid(x, y, fluid, width, height, new PoseStack());
    }

    // region DRAW METHODS
    public static void drawFluid(int x, int y, FluidStack fluid, int width, int height, PoseStack stack) {

        if (fluid.isEmpty()) {
            return;
        }
        RenderSystem.enableBlend();
        RenderSystem.blendFunc(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA);

        int color = fluid.getFluid().getAttributes().getColor(fluid);
        setPosTexShader();
        setBlockTextureSheet();
        setSahderColorFromInt(color);
        drawTiledTexture(x, y, getTexture(fluid.getFluid().getAttributes().getStillTexture(fluid)), width, height, stack);
    }

    public static void drawTiledTexture(int x, int y, TextureAtlasSprite icon, int width, int height, PoseStack stack) {

        int drawHeight;
        int drawWidth;

        for (int i = 0; i < width; i += 16) {
            for (int j = 0; j < height; j += 16) {
                drawWidth = Math.min(width - i, 16);
                drawHeight = Math.min(height - j, 16);
                drawScaledTexturedModalRectFromSprite(x + i, y + j, icon, drawWidth, drawHeight, stack);
            }
        }
        resetShaderColor();
    }

    public static void drawScaledTexturedModalRectFromSprite(int x, int y, TextureAtlasSprite icon, int width, int height, PoseStack stack) {

        if (icon == null) {
            return;
        }
        float minU = icon.getU0();
        float maxU = icon.getU1();
        float minV = icon.getV0();
        float maxV = icon.getV1();

        float u = minU + (maxU - minU) * width / 16F;
        float v = minV + (maxV - minV) * height / 16F;

        BufferBuilder buffer = tesselator().getBuilder();
        Matrix4f pose = stack.last().pose();
        buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
        buffer.vertex(pose, x, y + height, 0).uv(minU, v).endVertex();
        buffer.vertex(pose, x + width, y + height, 0).uv(u, v).endVertex();
        buffer.vertex(pose, x + width, y, 0).uv(u, minV).endVertex();
        buffer.vertex(pose, x, y, 0).uv(minU, minV).endVertex();
        tesselator().end();
    }

    public static TextureAtlasSprite getTexture(ResourceLocation location) {

        return textureMap().getSprite(location);
    }

    static {
        VertexFormat from = DefaultVertexFormat.BLOCK; //Always BLOCK as of 1.15

        List<VertexFormatElement> elements = from.getElements();
        for (int i = 0; i < from.getElements().size(); ++i) {
            VertexFormatElement element = elements.get(i);
            if (element.getUsage() == VertexFormatElement.Usage.COLOR) {
                break;
            }
        }
    }

    public static void setSahderColorFromInt(int color) {

        float red = (float) (color >> 16 & 255) / 255.0F;
        float green = (float) (color >> 8 & 255) / 255.0F;
        float blue = (float) (color & 255) / 255.0F;
        RenderSystem.setShaderColor(red, green, blue, 1.0F);
    }

    public static void setPosTexShader() {

        RenderSystem.setShader(GameRenderer::getPositionTexShader);
    }

    public static void resetShaderColor() {

        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
    }

    public static void setShaderTexture0(ResourceLocation texture) {

        RenderSystem.setShaderTexture(0, texture);
    }

    public static void renderGUIBlock(Block block, PoseStack stack, int x, int y) {
        renderGUIBlock(block, stack, x, y, 0);
    }

    public static void renderGUIBlock(Block block, PoseStack stack, int x, int y, int z) {
        MultiBufferSource.BufferSource buffersource = Minecraft.getInstance().renderBuffers().bufferSource();
        stack.pushPose();
        RenderSystem.enableBlend();
        RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA);
        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        //调整光照
        int light = block.getLightEmission(block.defaultBlockState(), Minecraft.getInstance().level, new BlockPos(1, 1, 1));
        if (light > 0) {
            Lighting.setupForFlatItems();
        } else {
            RenderSystem.setShaderLights(Util.make(new Vector3f(-0.2f, 1.2f, -0.7f), Vector3f::normalize), Util.make(new Vector3f(0.2f, -1.2f, 0.7f), Vector3f::normalize));
        }
        stack.translate(x + 16, y + 13, z + 10);
        stack.scale(-10, -10, -1);
        stack.mulPose(new Quaternion(Vector3f.XN, 30f, true));
        stack.mulPose(new Quaternion(Vector3f.YP, 45f, true));
        RenderSystem.applyModelViewMatrix();
        Minecraft.getInstance().getBlockRenderer().renderSingleBlock(block.defaultBlockState(), stack, buffersource, 0x00F0_00F0, OverlayTexture.NO_OVERLAY);
        buffersource.endBatch();
        stack.popPose();
    }

    public static void renderSlotHighlight(PoseStack pPoseStack, int x1, int y1, int width, int high, int pBlitOffset, int slotColor) {
        RenderSystem.disableDepthTest();
        RenderSystem.colorMask(true, true, true, false);
        fillGradient(pPoseStack, x1, y1, x1 + width, y1 + high, slotColor, slotColor, pBlitOffset);
        RenderSystem.colorMask(true, true, true, true);
        RenderSystem.enableDepthTest();
    }

    //拷贝自JEI
    public static void renderEntity(Entity entity, PoseStack matrixStack, int xPos, int yPos, float scale, float yRot) {
        matrixStack.pushPose();
        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        matrixStack.translate(xPos + 8, yPos + 24, 50.0D);
        matrixStack.scale(-scale, scale, scale);
        matrixStack.mulPose(Vector3f.ZP.rotationDegrees(180.0F));
        matrixStack.mulPose(Vector3f.YP.rotationDegrees(135.0F));
        Lighting.setupForEntityInInventory();
        matrixStack.mulPose(Vector3f.YP.rotationDegrees(-135.0F));
        matrixStack.mulPose(Vector3f.YP.rotationDegrees(yRot));
        matrixStack.mulPose(Vector3f.XP.rotationDegrees(0.0F));
        if (!(entity instanceof Player)) {
            entity.setXRot(0.0F);
            entity.xRotO = 0.0F;
            entity.setYRot(0.0F);
            entity.yRotO = 0.0F;
            if (entity instanceof LivingEntity livingEntity) {
                livingEntity.yBodyRotO = 0.0F;
                livingEntity.yBodyRot = 0.0F;
                livingEntity.yHeadRot = 0.0F;
                livingEntity.yHeadRotO = 0.0F;
            }
        }

        float ridingOffset;
        if (entity.getVehicle() == null) {
            ridingOffset = 0.0F;
        } else {
            ridingOffset = (float) entity.getMyRidingOffset();
        }

        matrixStack.translate(0.0D, ridingOffset + (entity instanceof HangingEntity ? 0.5F : 0.0F), 0.0D);
        RenderSystem.applyModelViewMatrix();
        EntityRenderDispatcher dispatcher = Minecraft.getInstance().getEntityRenderDispatcher();

        try {
            MultiBufferSource.BufferSource buffer = Minecraft.getInstance().renderBuffers().bufferSource();
            dispatcher.setRenderShadow(false);
            dispatcher.render(entity, 0.0D, 0.0D, 0.0D, 0.0F, 1.0F, matrixStack, buffer, 15728880);
            buffer.endBatch();
        } catch (Exception var8) {
            TheOneProbe.logger.error("Error rendering entity!", var8);
        }

        dispatcher.setRenderShadow(true);
        Lighting.setupFor3DItems();
        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
        RenderSystem.enableDepthTest();
        Minecraft.getInstance().gameRenderer.lightTexture().turnOffLightLayer();
        matrixStack.popPose();
        RenderSystem.applyModelViewMatrix();
    }

    //以下方法拷贝自Minecraft的实现，因被设为private或protected而重新实现
    private static void fillGradient(PoseStack pPoseStack, int pX1, int pY1, int pX2, int pY2, int pColorFrom, int pColorTo, int pBlitOffset) {
        RenderSystem.disableTexture();
        RenderSystem.enableBlend();
        RenderSystem.defaultBlendFunc();
        RenderSystem.setShader(GameRenderer::getPositionColorShader);
        Tesselator tesselator = Tesselator.getInstance();
        BufferBuilder bufferbuilder = tesselator.getBuilder();
        bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
        fillGradient(pPoseStack.last().pose(), bufferbuilder, pX1, pY1, pX2, pY2, pBlitOffset, pColorFrom, pColorTo);
        tesselator.end();
        RenderSystem.disableBlend();
        RenderSystem.enableTexture();
    }

    private static void fillGradient(Matrix4f pMatrix, BufferBuilder pBuilder, int pX1, int pY1, int pX2, int pY2, int pBlitOffset, int pColorA, int pColorB) {
        float f = (float) (pColorA >> 24 & 255) / 255.0F;
        float f1 = (float) (pColorA >> 16 & 255) / 255.0F;
        float f2 = (float) (pColorA >> 8 & 255) / 255.0F;
        float f3 = (float) (pColorA & 255) / 255.0F;
        float f4 = (float) (pColorB >> 24 & 255) / 255.0F;
        float f5 = (float) (pColorB >> 16 & 255) / 255.0F;
        float f6 = (float) (pColorB >> 8 & 255) / 255.0F;
        float f7 = (float) (pColorB & 255) / 255.0F;
        pBuilder.vertex(pMatrix, (float) pX2, (float) pY1, (float) pBlitOffset).color(f1, f2, f3, f).endVertex();
        pBuilder.vertex(pMatrix, (float) pX1, (float) pY1, (float) pBlitOffset).color(f1, f2, f3, f).endVertex();
        pBuilder.vertex(pMatrix, (float) pX1, (float) pY2, (float) pBlitOffset).color(f5, f6, f7, f4).endVertex();
        pBuilder.vertex(pMatrix, (float) pX2, (float) pY2, (float) pBlitOffset).color(f5, f6, f7, f4).endVertex();
    }

    public static boolean mouseOver(double mouseX, double mouseY, double x, double y, double width, double height) {
        return mouseX >= x && mouseX < x + width && mouseY >= y && mouseY < y + height;
    }
}